namespace EFS.EventLog.Performance
{
	/// <summary>
	/// Performance measuring by entrying events
	/// </summary>
	public class Marker<T>
	{
		// entry
		private class Entry
		{
			// time
			public System.DateTime Time { get; private set; }

			// descriptor
			public T Descriptor { get; private set; }

			// check point
			public System.Boolean CheckPoint { get; private set; }

			/// <summary>
			/// Contstructor
			/// </summary>
			public Entry(T descriptor, System.Boolean checkPoint)
			{
				// check
				EFS.Common.Verify.Reference(descriptor, "descriptor");

				// set
				Time = System.DateTime.Now;
				Descriptor = descriptor;
				CheckPoint = checkPoint;
			}

			/// <summary>
			/// Get string
			/// </summary>
			/// <returns></returns>
			public override System.String ToString()
			{
				// sb
				System.Text.StringBuilder sb = new System.Text.StringBuilder();

				// log
				sb.AppendLine(System.String.Format("		{0}", Time.ToString()));
				sb.AppendLine(System.String.Format("		{0}", Descriptor.ToString()));

				// done
				return sb.ToString();
			}
		}

		/// <summary>
		/// Time series
		/// </summary>
		private System.Collections.Generic.List<EFS.EventLog.Performance.Marker<T>.Entry> entries = new System.Collections.Generic.List<EFS.EventLog.Performance.Marker<T>.Entry>();

		/// <summary>
		/// Series
		/// </summary>
		private Series<T> series = null;
		
		/// <summary>
		/// Constructor
		/// </summary>
		internal Marker(Series<T> series, System.String markerDescription)
		{
			// check
			EFS.Common.Verify.Reference(series, "series");
			EFS.Common.Verify.String(markerDescription, "markerDescription");

			// set
			this.series = series;
			MarkerDescription = markerDescription;
		}

		/// <summary>
		/// Marker Name
		/// </summary>
		public System.String MarkerDescription { get; private set; }

		/// <summary>
		/// Add
		/// </summary>
		public void Add(T descriptor)
		{
			// check
			EFS.Common.Verify.Reference(descriptor, "descriptor");

			// add
			entries.Add(new EFS.EventLog.Performance.Marker<T>.Entry(descriptor, false));
		}

		/// <summary>
		/// Add
		/// </summary>
		public void AddCheckPoint(T descriptor)
		{
			// add
			entries.Add(new EFS.EventLog.Performance.Marker<T>.Entry(descriptor, true));
		}

		/// <summary>
		/// Write series and clean 
		/// </summary>
		public void Flush()
		{
			// write
			series.Write(this);

			// clean
			entries.Clear();
		}

		/// <summary>
		/// Make log string
		/// </summary>
		/// <returns></returns>
		public override System.String ToString()
		{
			// sb
			System.Text.StringBuilder sb = new System.Text.StringBuilder();

			// log process time span
			sb.AppendLine(System.String.Format("--> Performance Marker {0}", MarkerDescription));
			
			// add some space
			sb.AppendLine();

			// times
			EFS.EventLog.Performance.Marker<T>.Entry firstEntry = null;
			EFS.EventLog.Performance.Marker<T>.Entry checkPointEntry = null;
			EFS.EventLog.Performance.Marker<T>.Entry lastEntry = null;
			EFS.EventLog.Performance.Marker<T>.Entry currentEntry = null;

			// add
			foreach(EFS.EventLog.Performance.Marker<T>.Entry entry in entries)
			{
				// log
				sb.AppendLine(entry.ToString());

				// first 
				if (null == firstEntry)
				{
					firstEntry = entry;
				}

				// check point
				if (null == checkPointEntry)
				{
					checkPointEntry = entry;
				}

				// set last & current time
				lastEntry = currentEntry;
				currentEntry = entry;

				// local point
				if (null != lastEntry)
				{
					// compute time span
					sb.AppendLine(System.String.Format("--> Local Interval {0}", (currentEntry.Time - lastEntry.Time).ToString()));
				}

				// check point
				if (currentEntry.CheckPoint && null != checkPointEntry)
				{
					// compute time span
					sb.AppendLine(System.String.Format("--> Check point Interval {0}", (currentEntry.Time - checkPointEntry.Time).ToString()));
					
					// clear check point to be set as next
					checkPointEntry = currentEntry;
				}

				// add some space
				sb.AppendLine();
			}

			// Final
			if (null != currentEntry && null != firstEntry)
			{
				sb.AppendLine(System.String.Format("--> Final Interval {0}", (currentEntry.Time - firstEntry.Time).ToString()));
			}

			// done
			return sb.ToString();
		}
	}
}